package com.authsphere.security.account.common.lock;

import com.authsphere.common.utils.DateUtils;
import com.authsphere.security.account.api.lock.AccountLockoutService;
import com.authsphere.security.account.api.lock.LockedAccount;
import com.authsphere.security.account.api.lock.LockoutRepository;
import com.authsphere.security.account.api.lock.event.AccountLockedEvent;
import com.authsphere.security.account.common.config.SecurityAccountConfig;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @program: AuthSphere
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/14 09:37
 **/
public class DefaultAccountLockoutService implements AccountLockoutService, ApplicationEventPublisherAware, InitializingBean {

    private ApplicationEventPublisher eventPublisher;
    private final LockoutRepository lockoutRepository;
    private final SecurityAccountConfig.LockAccountConfig lockAccountConfig;

    public DefaultAccountLockoutService(SecurityAccountConfig.LockAccountConfig lockAccountConfig, LockoutRepository lockoutRepository) {
        this.lockAccountConfig = lockAccountConfig;
        this.lockoutRepository = lockoutRepository;
    }

    @Override
    public LockedAccount lockout(String username) {
        LockedAccount lockedAccount = Optional.ofNullable(lockoutRepository.findByUsername(username))
                .orElse(new LockedAccount(username));
        // 如果账号已经被锁定，直接返回
        if (lockedAccount.isLocked()) {
            return lockedAccount;
        }

        int failedAttempts = lockedAccount.incrementFailedAttempts();

        int[] maxAttempts = lockAccountConfig.getMaxAttempts();
        int[] lockForMinutes = lockAccountConfig.getLockForMinutes();
        int lockMinute = 0;
        int restAttempts = 0;
        for (int i = 0; i < maxAttempts.length; i++) {
            if (failedAttempts < maxAttempts[i]) {
                restAttempts = maxAttempts[i] - failedAttempts;
                break;
            }
            if (failedAttempts == maxAttempts[i]) {
                lockMinute = lockForMinutes[i];
                break;
            }
        }
        LocalDateTime now = LocalDateTime.now();
        lockedAccount.setRestAttempts(restAttempts);
        lockedAccount.setLastFailedTime(DateUtils.convertToMillis(now));
        lockedAccount.setLockMinutes(lockMinute);
        if (lockMinute < 0) {
            lockedAccount.setLockedPermanently(true);
        } else {
            lockedAccount.setUnlockTime(DateUtils.convertToMillis(now.plusMinutes(lockMinute)));
        }
        // 保存锁定记录
        lockoutRepository.save(lockedAccount);
        this.eventPublisher.publishEvent(new AccountLockedEvent(lockedAccount));
        return lockedAccount;
    }

    @Override
    public void unlock(String username) {
        Optional.ofNullable(lockoutRepository.findByUsername(username))
                .ifPresent(lockedAccount -> {
                    lockoutRepository.deleteByUsername(username);
                    this.eventPublisher.publishEvent(new AccountLockedEvent(lockedAccount));
                });
    }

    @Override
    public boolean isLocked(String username) {
        LockedAccount lockedAccount = lockoutRepository.findByUsername(username);
        if (Objects.isNull(lockedAccount)) {
            return false;
        }
        return lockedAccount.isLocked() || lockedAccount.isLockedPermanently();

    }

    @Override
    public int getLockedTime(String username) {
        LockedAccount lockedAccount = lockoutRepository.findByUsername(username);
        return Objects.isNull(lockedAccount) ? 0 : lockedAccount.getLockMinutes();
    }

    @Override
    public void resetAttemptCount(String username) {
        lockoutRepository.deleteByUsername(username);
    }

    @Override
    public List<LockedAccount> getLockedAccounts(String username) {
        return lockoutRepository.getLockedAccounts(username);
    }

    @Override
    public Pair<Long, List<LockedAccount>> pageLockedAccounts(int offset, int limit, String username) {
        List<String> allLockedAccounts = lockoutRepository.findAllLockedAccounts();
        List<LockedAccount> lockedAccounts = lockoutRepository.getLockedAccounts(username);
        return ImmutablePair.of((long) allLockedAccounts.size(), lockedAccounts);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.eventPublisher = applicationEventPublisher;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(lockAccountConfig, "LockAccountConfig can not be null");
        Assert.notNull(lockoutRepository, "LockoutRepository can not be null");
    }
}
